import random

import pygame

from src.base_elements.bullets import Bullet
from src.constants import Player, Direction, EnemyTankType


class TankImage:

    def __init__(self, image_path):
        self.image = pygame.image.load(image_path).convert_alpha()
        self.up1_image = self.image.subsurface((0, 0, 48, 48))
        self.up2_image = self.image.subsurface((48, 0, 48, 48))
        self.down1_image = self.image.subsurface((0, 48, 48, 48))
        self.down2_image = self.image.subsurface((48, 48, 48, 48))
        self.left1_image = self.image.subsurface((0, 96, 48, 48))
        self.left2_image = self.image.subsurface((48, 96, 48, 48))
        self.right1_image = self.image.subsurface((0, 144, 48, 48))
        self.right2_image = self.image.subsurface((48, 144, 48, 48))


class BaseTank(pygame.sprite.Sprite):
    # 坦克的最高射速, 单位：毫秒/发
    MAX_SHOOT_SPEED = 200

    def __init__(self):
        super().__init__()
        self.tank_image_obj = None
        self.image = None
        self.rect = None

        # 坦克的等级
        self.level = None
        # 坦克的速度，单位：像素点/祯
        self.speed = 3
        # 坦克的朝向
        self.direction = None
        # 坦克的射速, 单位：毫秒/发
        self.shoot_speed = None
        # 坦克上一次射击时间
        self.last_shoot_time = None
        # # 坦克子弹的速度，单位：像素点/祯
        self.bullet_speed = None
        # 坦克子弹能否摧毁钢铁
        self.can_destroy_steel = False
        # 坦克移动动画间隔，单位：毫秒
        self.move_animation_interval = 200 / self.speed
        # 上一次坦克移动动画变化的时间
        self.last_move_animation_change_time = 0
        # 坦克的子弹
        self.bullet = None

    def _control_move_animation(self, direction):
        if pygame.time.get_ticks() - self.last_move_animation_change_time > self.move_animation_interval or \
                self.direction != direction:
            if direction == Direction.UP:
                if self.image == self.tank_image_obj.up2_image:
                    self.image = self.tank_image_obj.up1_image
                else:
                    self.image = self.tank_image_obj.up2_image
                self.last_move_animation_change_time = pygame.time.get_ticks()
                self.direction = direction
            if direction == Direction.DOWN:
                if self.image == self.tank_image_obj.down2_image:
                    self.image = self.tank_image_obj.down1_image
                else:
                    self.image = self.tank_image_obj.down2_image
                self.last_move_animation_change_time = pygame.time.get_ticks()
                self.direction = direction
            if direction == Direction.LEFT:
                if self.image == self.tank_image_obj.left2_image:
                    self.image = self.tank_image_obj.left1_image
                else:
                    self.image = self.tank_image_obj.left2_image
                self.last_move_animation_change_time = pygame.time.get_ticks()
                self.direction = direction
            if direction == Direction.RIGHT:
                if self.image == self.tank_image_obj.right2_image:
                    self.image = self.tank_image_obj.right1_image
                else:
                    self.image = self.tank_image_obj.right2_image
                self.last_move_animation_change_time = pygame.time.get_ticks()
                self.direction = direction

    def move(self, direction=None, tank_group=None, landscape_group=None, animation_group=None):
        """
        该方法实现坦克的移动以及移动中的碰撞检测。
        """
        last_position = (self.rect.x, self.rect.y)
        self._control_move_animation(direction)
        self.rect = self.rect.move(self.speed * direction[0], self.speed * direction[1])
        # 检查坦克与边界的碰撞
        if self.rect.top < 3:
            self.rect.top = 3
            return False, None
        if self.rect.bottom > 630 - 3:
            self.rect.bottom = 630 - 3
            return False, None
        if self.rect.left < 3:
            self.rect.left = 3
            return False, None
        if self.rect.right > 630 - 3:
            self.rect.right = 630 - 3
            return False, None
        # 检测坦克与坦克的碰撞，其中坦克与坦克的碰撞检测会返回它自己，因此当大于 1 时才表示碰撞到了坦克。
        collided_tanks = pygame.sprite.spritecollide(self, tank_group, False)
        if len(collided_tanks) > 1:
            self.rect.x, self.rect.y = last_position
            return False, collided_tanks
        # 检测坦克与地形的碰撞
        landscapes = pygame.sprite.spritecollide(self, landscape_group, False)
        for landscape in landscapes:
            if not landscape.can_cross_by_tank:
                self.rect.x, self.rect.y = last_position
                return False, landscapes
        # 检测坦克与某些动画的碰撞
        collided_animations = pygame.sprite.spritecollide(self, animation_group, False)
        if collided_animations:
            self.rect.x, self.rect.y = last_position
            return False, collided_animations
        return True, None

    def shoot(self, is_fast_shoot=False):
        if is_fast_shoot:
            if pygame.time.get_ticks() - self.last_shoot_time < self.MAX_SHOOT_SPEED:
                return None
        if not is_fast_shoot:
            if pygame.time.get_ticks() - self.last_shoot_time < self.shoot_speed:
                return None
        self.last_shoot_time = pygame.time.get_ticks()
        self.bullet = Bullet(self.direction, self.rect, speed=self.bullet_speed,
                        can_destroy_steel=self.can_destroy_steel)
        return self.bullet


class PlayerTank(BaseTank):

    def __init__(self, player=None, gunfire_sound=None):
        super().__init__()
        self.player = player
        self._load_tank_image()
        self.image = self.tank_image_obj.up1_image
        self.rect = self.image.get_rect()
        if self.player == Player.PLAYER1:
            self.rect.left = 3 + 4 * 48
            self.rect.top = 3 + 12 * 48
        if self.player == Player.PLAYER2:
            self.rect.left = 3 + 8 * 48
            self.rect.top = 3 + 12 * 48
        self.gunfire_sound = gunfire_sound

        # 坦克的速度，单位：像素点/祯
        self.speed = 3
        # 坦克的朝向
        self.direction = Direction.UP
        # 坦克的射速, 单位：毫秒/发
        self.shoot_speed = 1000
        # 坦克上一次射击时间
        self.last_shoot_time = 0
        # 坦克子弹的速度，单位：像素点/祯
        self.bullet_speed = 6
        # 坦克子弹能否摧毁钢铁
        self.can_destroy_steel = False
        # 护盾
        self.shield = None
        # 护盾生效时间
        self.shield_active_time = 0
        # 护盾持续时间, 单位：毫秒
        self.shield_duration = 20000
        # 坦克的等级
        self.level = 1
        # 坦克等级和 [图像、坦克射速、 坦克子弹速度、是否能摧毁钢铁] 的映射
        # 子弹最高速度不能超过 12 像素/帧，否则会出现坦克贴着砖块射击时，因为子弹速度太快，直接飞过坦克面前的砖块而不触发碰撞检测。
        self.level_map = {
            1: [self.lv1_tank_image_obj, 1000, 6, False],
            2: [self.lv2_tank_image_obj, 1000, 10, False],
            3: [self.lv3_tank_image_obj, 500, 10, False],
            4: [self.lv4_tank_image_obj, 500, 10, True],
        }

    def _load_tank_image(self):
        if self.player == Player.PLAYER1:
            self.lv1_tank_image_obj = TankImage(r"./assets/images/tanks/player1_lv1_tank.png")
            self.lv2_tank_image_obj = TankImage(r"./assets/images/tanks/player1_lv2_tank.png")
            self.lv3_tank_image_obj = TankImage(r"./assets/images/tanks/player1_lv3_tank.png")
            self.lv4_tank_image_obj = TankImage(r"./assets/images/tanks/player1_lv4_tank.png")
        elif self.player == Player.PLAYER2:
            self.lv1_tank_image_obj = TankImage(r"./assets/images/tanks/player2_lv1_tank.png")
            self.lv2_tank_image_obj = TankImage(r"./assets/images/tanks/player2_lv2_tank.png")
            self.lv3_tank_image_obj = TankImage(r"./assets/images/tanks/player2_lv3_tank.png")
            self.lv4_tank_image_obj = TankImage(r"./assets/images/tanks/player2_lv4_tank.png")
        self.tank_image_obj = self.lv1_tank_image_obj

    def _load_sound(self):
        self.upgrade_sound = pygame.mixer.Sound("assets/sounds/bullet_impact.ogg")

    def move(self, prop_group=None, **kwargs):
        can_move, collided_sprites = super().move(**kwargs)
        if self.shield:
            self.shield.move(**kwargs)
        if not can_move:
            return can_move, collided_sprites
        # 检测坦克与道具的碰撞
        props = pygame.sprite.spritecollide(self, prop_group, True)
        if props:
            return False, props
        return True, None

    def shoot(self, is_fast_shoot=False):
        if not self.bullet or not self.bullet.is_alive:
            is_fast_shoot = True
        bullet = super().shoot(is_fast_shoot=is_fast_shoot)
        if bullet:
            bullet.owner = self
            self.gunfire_sound.play()
        return bullet

    def upgrade(self, is_upgrade_top=False):
        if self.level == 4:
            return
        if is_upgrade_top:
            self.level = 4
            self.tank_image_obj = self.level_map[4][0]
            self.shoot_speed = self.level_map[4][1]
            self.bullet_speed = self.level_map[4][2]
            self.can_destroy_steel = self.level_map[4][3]
            return
        self.level += 1
        tank_stats = self.level_map[self.level]
        self.tank_image_obj = tank_stats[0]
        self.shoot_speed = tank_stats[1]
        self.bullet_speed = tank_stats[2]
        self.can_destroy_steel = tank_stats[3]

    def reset(self):
        self.image = self.tank_image_obj.up1_image
        if self.player == Player.PLAYER1:
            self.rect.topleft = (3 + 4 * 48, 3 + 12 * 48)
        else:
            self.rect.topleft = (3 + 8 * 48, 3 + 12 * 48)


class Shield(pygame.sprite.Sprite):

    def __init__(self, speed):
        super().__init__()
        self.last_move_animation_change_time = 0
        self.move_animation_interval = 400
        self.speed = speed

        self.shield_image_01 = pygame.image.load(r"./assets/images/tanks/shield_1.png").convert_alpha()
        self.shield_image_02 = pygame.image.load(r"./assets/images/tanks/shield_2.png").convert_alpha()
        self.image = self.shield_image_01
        self.rect = self.image.get_rect()

    def move(self, direction=None, tank_group=None, landscape_group=None, animation_group=None):
        last_position = (self.rect.x, self.rect.y)

        self.rect = self.rect.move(self.speed * direction[0], self.speed * direction[1])
        # 检查坦克与边界的碰撞
        if self.rect.top < 3:
            self.rect.top = 3
            return False, None
        if self.rect.bottom > 630 - 3:
            self.rect.bottom = 630 - 3
            return False, None
        if self.rect.left < 3:
            self.rect.left = 3
            return False, None
        if self.rect.right > 630 - 3:
            self.rect.right = 630 - 3
            return False, None
        # 检测坦克与坦克的碰撞，其中坦克与坦克的碰撞检测会返回它自己，因此当大于 1 时才表示碰撞到了坦克。
        collided_tanks = pygame.sprite.spritecollide(self, tank_group, False)
        if len(collided_tanks) > 1:
            self.rect.x, self.rect.y = last_position
            return False, collided_tanks
        # 检测坦克与地形的碰撞
        landscapes = pygame.sprite.spritecollide(self, landscape_group, False)
        for landscape in landscapes:
            if not landscape.can_cross_by_tank:
                self.rect.x, self.rect.y = last_position
                return False, landscapes
        # 检测坦克与某些动画的碰撞
        collided_animations = pygame.sprite.spritecollide(self, animation_group, False)
        if collided_animations:
            self.rect.x, self.rect.y = last_position
            return False, collided_animations

        return True, None


class BaseEnemyTank(BaseTank):

    def __init__(self):
        super().__init__()
        # 敌方坦克类型
        self.type = None

        # 坦克随机移动多少距离后，改变移动方向
        self.random_distances = [144, 288, 432, 576, 576]
        # 当前坦克需要随机移动的距离
        self.current_random_distance = 144

        # 坦克随机连续射击次数，-2 表示两秒不射击
        self.random_shoot_counts = [3, 5, -1, -2]
        # 当前坦克需要射击的次数，-2 表示两秒不射击
        self.current_random_shoot_count = -2

    def auto_move(self, tank_group, landscape_group, animation_group):
        can_move, _ = self.move(
            direction=self.direction,
            tank_group=tank_group,
            landscape_group=landscape_group,
            animation_group=animation_group
        )
        self.current_random_distance -= 1
        if not can_move or self.current_random_distance == 0:
            directions = list(Direction())
            directions.remove(self.direction)
            self.direction = random.choice(directions)
            self.current_random_distance = random.choice(self.random_distances)

    def auto_shoot(self):
        if self.current_random_shoot_count < 0:
            abandon_shoot_time = abs(self.current_random_shoot_count)
            if pygame.time.get_ticks() - self.last_shoot_time > abandon_shoot_time * 1000:
                self.current_random_shoot_count = random.choice(self.random_shoot_counts)
        elif self.current_random_shoot_count > 0:
            bullet = self.shoot()
            if bullet:
                self.current_random_shoot_count -= 1
                return bullet
        else:
            self.current_random_shoot_count = random.choice(self.random_shoot_counts)
        return None


class Enemy1Tank(BaseEnemyTank):
    # 敌方坦克的分数
    score = 100

    def __init__(self):
        super().__init__()
        self._load_tank_image()
        self.image = self.tank_image_obj.down1_image
        self.rect = self.image.get_rect()

        # 坦克的速度，单位：像素点/祯
        self.speed = 1
        # 坦克的朝向
        self.direction = Direction.DOWN
        # 坦克的射速, 单位：毫秒/发
        self.shoot_speed = 1000
        # 坦克上一次射击时间
        self.last_shoot_time = 0
        # # 坦克子弹的速度，单位：像素点/祯
        self.bullet_speed = 6
        # 坦克等级
        self.level = 1
        # 敌方坦克类型
        self.type = EnemyTankType.ENEMY_01

    def _load_tank_image(self):
        self.lv1_tank_image_obj = TankImage(r"./assets/images/tanks/enemy1_lv1_tank.png")
        self.tank_image_obj = self.lv1_tank_image_obj


class Enemy2Tank(BaseEnemyTank):
    # 敌方坦克的分数
    score = 200

    def __init__(self):
        super().__init__()
        self._load_tank_image()
        self.image = self.tank_image_obj.down1_image
        self.rect = self.image.get_rect()

        # 坦克的速度，单位：像素点/祯
        self.speed = 3
        # 坦克的朝向
        self.direction = Direction.DOWN
        # 坦克的射速, 单位：毫秒/发
        self.shoot_speed = 1000
        # 坦克上一次射击时间
        self.last_shoot_time = 0
        # # 坦克子弹的速度，单位：像素点/祯
        self.bullet_speed = 6
        # 坦克等级
        self.level = 1
        # 敌方坦克类型
        self.type = EnemyTankType.ENEMY_02

    def _load_tank_image(self):
        self.lv1_tank_image_obj = TankImage(r"./assets/images/tanks/enemy2_lv1_tank.png")
        self.tank_image_obj = self.lv1_tank_image_obj


class Enemy3Tank(BaseEnemyTank):
    # 敌方坦克的分数
    score = 300

    def __init__(self):
        super().__init__()
        self._load_tank_image()
        self.image = self.tank_image_obj.down1_image
        self.rect = self.image.get_rect()

        # 坦克的速度，单位：像素点/祯
        self.speed = 2
        # 坦克的朝向
        self.direction = Direction.DOWN
        # 坦克的射速, 单位：毫秒/发
        self.shoot_speed = 500
        # 坦克上一次射击时间
        self.last_shoot_time = 0
        # # 坦克子弹的速度，单位：像素点/祯
        self.bullet_speed = 6
        # 坦克等级
        self.level = 1
        # 敌方坦克类型
        self.type = EnemyTankType.ENEMY_03

    def _load_tank_image(self):
        self.lv1_tank_image_obj = TankImage(r"./assets/images/tanks/enemy3_lv1_tank.png")
        self.tank_image_obj = self.lv1_tank_image_obj


class Enemy4Tank(BaseEnemyTank):
    # 敌方坦克的分数
    score = 400

    def __init__(self):
        super().__init__()
        self._load_tank_image()
        self.image = self.tank_image_obj.down1_image
        self.rect = self.image.get_rect()

        # 坦克的速度，单位：像素点/祯
        self.speed = 1
        # 坦克的朝向
        self.direction = Direction.DOWN
        # 坦克的射速, 单位：毫秒/发
        self.shoot_speed = 1000
        # 坦克上一次射击时间
        self.last_shoot_time = 0
        # # 坦克子弹的速度，单位：像素点/祯
        self.bullet_speed = 6
        # 坦克等级
        self.level = 3
        # 坦克等级和图像的映射
        self.level_map = {
            1: self.lv1_tank_image_obj,
            2: self.lv2_tank_image_obj,
            3: self.lv3_tank_image_obj,
        }
        # 敌方坦克类型
        self.type = EnemyTankType.ENEMY_04

    def _load_tank_image(self):
        self.lv1_tank_image_obj = TankImage(r"./assets/images/tanks/enemy4_lv1_tank.png")
        self.lv2_tank_image_obj = TankImage(r"./assets/images/tanks/enemy4_lv2_tank.png")
        self.lv3_tank_image_obj = TankImage(r"./assets/images/tanks/enemy4_lv3_tank.png")
        self.tank_image_obj = self.lv3_tank_image_obj

    def change_level(self, level):
        if level not in self.level_map:
            print("Invalid tank level.")
        self.level = level
        self.tank_image_obj = self.level_map.get(self.level)


class EnemyPropTank(BaseEnemyTank):
    # 敌方坦克的分数
    score = 100

    def __init__(self):
        super().__init__()
        self._load_tank_image()
        self.image = self.tank_image_obj.down1_image
        self.rect = self.image.get_rect()

        # 坦克的速度，单位：像素点/祯
        self.speed = 1
        # 坦克的朝向
        self.direction = Direction.DOWN
        # 坦克的射速, 单位：毫秒/发
        self.shoot_speed = 1000
        # 坦克上一次射击时间
        self.last_shoot_time = 0
        # # 坦克子弹的速度，单位：像素点/祯
        self.bullet_speed = 6
        # 坦克等级
        self.level = 1
        # 敌方坦克类型
        self.type = EnemyTankType.ENEMY_PROP
        # 用来控制奖励坦克闪光的变量
        self.idx = 0

    def _load_tank_image(self):
        self.lv1_tank_image_obj = TankImage(r"./assets/images/tanks/enemy1_lv1_tank.png")
        self.prop_tank_image_obj = TankImage(r"./assets/images/tanks/enemy1_prop_tank.png")
        self.tank_image_obj = self.lv1_tank_image_obj

    def _control_move_animation(self, direction):
        if pygame.time.get_ticks() - self.last_move_animation_change_time > self.move_animation_interval or \
                self.direction != direction:
            if direction == Direction.UP:
                if self.image == self.tank_image_obj.up2_image:
                    self.image = self.tank_image_obj.up1_image
                else:
                    self.image = self.tank_image_obj.up2_image
                self.last_move_animation_change_time = pygame.time.get_ticks()
                self.direction = direction
            if direction == Direction.DOWN:
                if self.image == self.tank_image_obj.down2_image:
                    self.image = self.tank_image_obj.down1_image
                else:
                    self.image = self.tank_image_obj.down2_image
                self.last_move_animation_change_time = pygame.time.get_ticks()
                self.direction = direction
            if direction == Direction.LEFT:
                if self.image == self.tank_image_obj.left2_image:
                    self.image = self.tank_image_obj.left1_image
                else:
                    self.image = self.tank_image_obj.left2_image
                self.last_move_animation_change_time = pygame.time.get_ticks()
                self.direction = direction
            if direction == Direction.RIGHT:
                if self.image == self.tank_image_obj.right2_image:
                    self.image = self.tank_image_obj.right1_image
                else:
                    self.image = self.tank_image_obj.right2_image
                self.last_move_animation_change_time = pygame.time.get_ticks()
                self.direction = direction
            self.idx += 1
            if self.idx > 1:
                if self.tank_image_obj == self.prop_tank_image_obj:
                    self.tank_image_obj = self.lv1_tank_image_obj
                else:
                    self.tank_image_obj = self.prop_tank_image_obj
                self.idx = 0


TANK_MAP = {
    '1': Enemy1Tank,
    '2': Enemy2Tank,
    '3': Enemy3Tank,
    '4': Enemy4Tank,
    '5': EnemyPropTank,
}
